// streambuf standard header -*-c++-*-
// Copyright 2009-2010 IAR Systems AB.
#ifndef _STREAMBUF_
#define _STREAMBUF_

#ifndef _SYSTEM_BUILD
#pragma system_include
#endif

#include <xiosbase>
_STD_BEGIN

// CLASS streambuf
class streambuf
{       // control read/write buffers
  streambuf(const streambuf&);  // not defined
  streambuf& operator=(const streambuf&);       // not defined

protected:
  streambuf()
    : _Plocale(_NEW_CRT locale)
  {     // construct with no buffers
    _Init();
  }

  streambuf(_Uninitialized)
  {     // construct uninitialized
  }

public:
  typedef char _Elem;
  typedef char_traits _Traits;
  typedef streambuf _Myt;
  typedef _Elem char_type;
  typedef _Traits traits_type;

  virtual ~streambuf()
  {     // destroy the object
    _DELETE_CRT(_Plocale);
  }

  typedef _Traits::int_type int_type;
  typedef _Traits::pos_type pos_type;
  typedef _Traits::off_type off_type;

  pos_type pubseekoff(off_type _Off, ios_base::seekdir _Way,
                      ios_base::openmode _Mode = ios_base::in | ios_base::out)
  {     // change position by _Off, according to _Way, _Mode
    return (seekoff(_Off, _Way, _Mode));
  }

  pos_type pubseekoff(off_type _Off, ios_base::seek_dir _Way,
                      ios_base::open_mode _Mode)
  {     // change position by _Off, according to _Way, _Mode (old style)
    return (pubseekoff(_Off, (ios_base::seekdir)_Way,
                       (ios_base::openmode)_Mode));
  }

  pos_type pubseekpos(pos_type _Pos,
                      ios_base::openmode _Mode = ios_base::in | ios_base::out)
  {     // change position to _Pos, according to _Mode
    return (seekpos(_Pos, _Mode));
  }

  pos_type pubseekpos(pos_type _Pos, ios_base::open_mode _Mode)
  {     // change position to _Pos, according to _Mode (old style)
    return (seekpos(_Pos, (ios_base::openmode)_Mode));
  }

  _Myt *pubsetbuf(_Elem *_Buffer, streamsize _Count)
  {     // offer _Buffer to external agent
    return (setbuf(_Buffer, _Count));
  }

  locale pubimbue(const locale &_Newlocale)
  {     // set locale to argument
    locale _Oldlocale = *_Plocale;
    imbue(_Newlocale);
    *_Plocale = _Newlocale;
    return (_Oldlocale);
  }

  locale getloc() const
  {     // get locale
    return (*_Plocale);
  }

  streamsize in_avail()
  {     // return count of buffered input characters
    streamsize _Res = _Gnavail();
    return (0 < _Res ? _Res : showmanyc());
  }

  int pubsync()
  {     // synchronize with external agent
    return (sync());
  }

  int_type sbumpc()
  {     // get a character and point past it
    return (0 < _Gnavail()
            ? _Traits::to_int_type(*_Gninc()) : uflow());
  }

  int_type sgetc()
  {     // get a character and don't point past it
    return (0 < _Gnavail()
            ? _Traits::to_int_type(*gptr()) : underflow());
  }

  streamsize sgetn(_Elem *_Ptr, streamsize _Count)
  {     // get up to _Count characters into array beginning at _Ptr
    return (xsgetn(_Ptr, _Count));
  }

  int_type snextc()
  {     // point to next character and return it
    return (1 < _Gnavail()
            ? _Traits::to_int_type(*_Gnpreinc())
            : _Traits::eq_int_type(_Traits::eof(), sbumpc())
            ? _Traits::eof() : sgetc());
  }

  int_type sputbackc(_Elem _Ch)
  {     // put back _Ch
    return (gptr() != 0 && eback() < gptr()
            && _Traits::eq(_Ch, gptr()[-1])
            ? _Traits::to_int_type(*_Gndec())
            : pbackfail(_Traits::to_int_type(_Ch)));
  }

  void stossc()
  {     // point past a character
    if (0 < _Gnavail())
      _Gninc();
    else
      uflow();
  }

  int_type sungetc()
  {     // back up one position
    return (gptr() != 0 && eback() < gptr()
            ? _Traits::to_int_type(*_Gndec()) : pbackfail());
  }

  int_type sputc(_Elem _Ch)
  {     // put a character
    return (0 < _Pnavail()
            ? _Traits::to_int_type(*_Pninc() = _Ch)
            : overflow(_Traits::to_int_type(_Ch)));
  }

  streamsize sputn(const _Elem *_Ptr, streamsize _Count)
  {     // put _Count characters from array beginning at _Ptr
    return (xsputn(_Ptr, _Count));
  }

  virtual void _Lock()
  {     // set the thread lock
    _Mylock._Lock();
  }

  virtual void _Unlock()
  {     // clear the thread lock
    _Mylock._Unlock();
  }

protected:
  _Elem *eback() const
  {     // return beginning of read buffer
    return (*_IGfirst);
  }

  _Elem *gptr() const
  {     // return current position in read buffer
    return (*_IGnext);
  }

  _Elem *pbase() const
  {     // return beginning of write buffer
    return (*_IPfirst);
  }

  _Elem *pptr() const
  {     // return current position in write buffer
    return (*_IPnext);
  }

  _Elem *egptr() const
  {     // return end of read buffer
    return (*_IGlast);
  }

  void gbump(int _Off)
  {     // alter current position in read buffer by _Off
    *_IGnext += _Off;
  }

  void setg(_Elem *_First, _Elem *_Next, _Elem *_Last)
  {     // set pointers for read buffer
    *_IGfirst = _First, *_IGnext = _Next, *_IGlast = _Last;
  }

  _Elem *epptr() const
  {     // return end of write buffer
    return (*_IPlast);
  }

  _Elem *_Gndec()
  {     // decrement current position in read buffer
    return (--*_IGnext);
  }

  _Elem *_Gninc()
  {     // increment current position in read buffer
    return ((*_IGnext)++);
  }

  _Elem *_Gnpreinc()
  {     // preincrement current position in read buffer
    return (++(*_IGnext));
  }

  streamsize _Gnavail() const
  {       // count number of available elements in read buffer
    return ((streamsize)(*_IGnext != 0 ? *_IGlast - *_IGnext : 0));
  }

  void pbump(int _Off)
  {     // alter current position in write buffer by _Off
    *_IPnext += _Off;
  }

  void setp(_Elem *_First, _Elem *_Last)
  {     // set pointers for write buffer
    *_IPfirst = _First, *_IPnext = _First, *_IPlast = _Last;
  }

  void setp(_Elem *_First, _Elem *_Next, _Elem *_Last)
  {     // set pointers for write buffer, extended version
    *_IPfirst = _First, *_IPnext = _Next, *_IPlast = _Last;
  }

  _Elem *_Pninc()
  {     // increment current position in write buffer
    return ((*_IPnext)++);
  }

  streamsize _Pnavail() const
  {       // count number of available positions in write buffer
    return ((streamsize)(*_IPnext != 0 ? *_IPlast - *_IPnext : 0));
  }

  void _Init()
  {     // initialize buffer parameters for no buffers
    _IGfirst = &_Gfirst, _IPfirst = &_Pfirst;
    _IGnext = &_Gnext, _IPnext = &_Pnext;
    _IGlast = &_Glast, _IPlast = &_Plast;
    setp(0, 0), setg(0, 0, 0);
  }

  void _Init(_Elem **_Gf, _Elem **_Gn, _Elem **_Gl,
             _Elem **_Pf, _Elem **_Pn, _Elem **_Pl)
  {     // initialize buffer parameters as specified
    _IGfirst = _Gf, _IPfirst = _Pf;
    _IGnext = _Gn, _IPnext = _Pn;
    _IGlast = _Gl, _IPlast = _Pl;
  }

  virtual int_type overflow(int_type = _Traits::eof())
  {     // put a character to stream (always fail)
    return (_Traits::eof());
  }

  virtual int_type pbackfail(int_type = _Traits::eof())
  {     // put a character back to stream (always fail)
    return (_Traits::eof());
  }

  virtual streamsize showmanyc()
  {     // return count of input characters
    return (0);
  }

  virtual int_type underflow()
  {     // get a character from stream, but don't point past it
    return (_Traits::eof());
  }

  virtual int_type uflow()
  {     // get a character from stream, point past it
    return (_Traits::eq_int_type(_Traits::eof(), underflow())
            ? _Traits::eof() : _Traits::to_int_type(*_Gninc()));
  }

  virtual streamsize xsgetn(_Elem * _Ptr, streamsize _Count)
  {     // get _Count characters from stream
    int_type _Meta;
    streamsize _Size, _Copied;

    for (_Copied = 0; 0 < _Count; )
      if (0 < (_Size = _Gnavail()))
      {       // copy from read buffer
        if (_Count < _Size)
          _Size = _Count;
        _Traits::copy(_Ptr, gptr(), _Size);
        _Ptr += _Size;
        _Copied += _Size;
        _Count -= _Size;
        gbump((int)_Size);
      }
      else if (_Traits::eq_int_type(_Traits::eof(), _Meta = uflow()))
        break;  // end of file, quit
      else
      {       // get a single character
        *_Ptr++ = _Traits::to_char_type(_Meta);
        ++_Copied;
        --_Count;
      }

    return (_Copied);
  }

  virtual streamsize xsputn(const _Elem *_Ptr, streamsize _Count)
  {     // put _Count characters to stream
    streamsize _Size, _Copied;

    for (_Copied = 0; 0 < _Count; )
      if (0 < (_Size = _Pnavail()))
      {       // copy to write buffer
        if (_Count < _Size)
          _Size = _Count;
        _Traits::copy(pptr(), _Ptr, _Size);
        _Ptr += _Size;
        _Copied += _Size;
        _Count -= _Size;
        pbump((int)_Size);
      }
      else if (_Traits::eq_int_type(_Traits::eof(),
                                    overflow(_Traits::to_int_type(*_Ptr))))
        break;  // single character put failed, quit
      else
      {       // count character successfully put
        ++_Ptr;
        ++_Copied;
        --_Count;
      }
    return (_Copied);
  }

  virtual pos_type seekoff(off_type, ios_base::seekdir,
                           ios_base::openmode = ios_base::in | ios_base::out)
  {     // change position by offset, according to way and mode
    return (streampos(_BADOFF));
  }

  virtual pos_type seekpos(pos_type,
                           ios_base::openmode = ios_base::in | ios_base::out)
  {     // change to specified position, according to mode
    return (streampos(_BADOFF));
  }

  virtual _Myt *setbuf(_Elem *, streamsize)
  {     // offer buffer to external agent (do nothing)
    return (this);
  }

  virtual int sync()
  {     // synchronize with external agent (do nothing)
    return (0);
  }

  virtual void imbue(const locale&)
  {     // set locale to argument (do nothing)
  }

private:
  __iar_Mutex _Mylock;  // thread lock
  _Elem *_Gfirst;       // beginning of read buffer
  _Elem *_Pfirst;       // beginning of write buffer
  _Elem **_IGfirst;     // pointer to beginning of read buffer
  _Elem **_IPfirst;     // pointer to beginning of write buffer
  _Elem *_Gnext;        // current position in read buffer
  _Elem *_Pnext;        // current position in write buffer
  _Elem **_IGnext;      // pointer to current position in read buffer
  _Elem **_IPnext;      // pointer to current position in write buffer
  _Elem *_Glast;        // end of read buffer
  _Elem *_Plast;        // end of write buffer
  _Elem **_IGlast;      // pointer to end of read buffer
  _Elem **_IPlast;      // pointer to end of write buffer
  locale *_Plocale;     // pointer to imbued locale object
};


// CLASS _Istrit
class _Istrit
{       // input iterator for reading istream
public:
  typedef _Istrit _Myt;
  typedef char char_type;
  typedef char_traits traits_type;
  typedef traits_type::int_type int_type;
  typedef streambuf streambuf_type;

  _Istrit(streambuf_type *_Sb = 0) _THROW0()
  : _Strbuf(_Sb), _Got(_Sb == 0)
  {     // construct iterator for stream buffer _Sb
  }

  const char_type& operator*() const
  {     // return designated value
    if (!_Got)
      ((_Myt *)this)->_Peek();
    return (_Val);
  }

  _Myt& operator++()
  {     // preincrement
    _Inc();
    return (*this);
  }

  _Myt operator++(int)
  {     // postincrement
    if (!_Got)
      _Peek();
    _Myt _Tmp = *this;
    _Inc();
    return (_Tmp);
  }

  bool equal(const _Myt& _Right) const
  {     // test if equal to _Right
    if (!_Got)
      ((_Myt *)this)->_Peek();
    if (!_Right._Got)
      ((_Myt *)&_Right)->_Peek();
    return (_Strbuf == 0 && _Right._Strbuf == 0
            || _Strbuf != 0 && _Right._Strbuf != 0);
  }

private:
  void _Inc()
  {     // point past current element
    if (_Strbuf == 0
        || traits_type::eq_int_type(traits_type::eof(),
                                    _Strbuf->sbumpc()))
      _Strbuf = 0, _Got = true;
    else
      _Got = false;
  }

  char_type _Peek()
  {     // peek at current element
    int_type _Meta;
    if (_Strbuf == 0
        || traits_type::eq_int_type(traits_type::eof(),
                                    _Meta = _Strbuf->sgetc()))
      _Strbuf = 0;
    else
      _Val = traits_type::to_char_type(_Meta);
    _Got = true;
    return (_Val);
  }
  
  streambuf_type *_Strbuf;      // the stream buffer to read
  bool _Got;    // true if _Val is valid
  char_type _Val;       // current element value
};

// _Istrit OPERATORS
inline bool operator==(const _Istrit& _Left, const _Istrit& _Right)
{       // test for iterator equality
  return (_Left.equal(_Right));
}

inline bool operator!=(const _Istrit& _Left, const _Istrit& _Right)
{       // test for iterator inequality
  return (!(_Left == _Right));
}

                // CLASS _Ostrit
class _Ostrit
{       // output iterator for writing ostream
public:
  typedef _Ostrit _Myt;
  typedef char char_type;
  typedef char_traits traits_type;
  typedef streambuf streambuf_type;
  
  _Ostrit(streambuf_type *_Sb) _THROW0()
  : _Failed(false), _Strbuf(_Sb)
  {     // construct iterator for stream buffer _Sb
  }

  _Myt& operator=(char_type _Right)
  {     // store element and increment
    if (_Strbuf == 0
        || traits_type::eq_int_type(traits_type::eof(),
                                    _Strbuf->sputc(_Right)))
      _Failed = true;
    return (*this);
  }
  
  _Myt& operator*()
  {     // pretend to get designated element
    return (*this);
  }
  
  _Myt& operator++()
  {     // pretend to preincrement
    return (*this);
  }
  
  _Myt& operator++(int)
  {     // pretend to postincrement
    return (*this);
  }
  
  bool failed() const _THROW0()
  {     // report true only if any stores failed
    return (_Failed);
  }
  
private:
  bool _Failed; // true if any stores failed
  streambuf_type *_Strbuf;      // the stream buffer to write
};

_STD_END
#endif /* _STREAMBUF_ */

/*
 * Copyright (c) 1992-2009 by P.J. Plauger.  ALL RIGHTS RESERVED.
 * Consult your license regarding permissions and restrictions.
 V5.04:0576 */
